use anyhow::Result;
use tokio_stream::StreamExt;
use matrix_sdk::{
    encryption::verification::{format_emojis, Emoji, SasState, SasVerification, Verification},
    ruma::{
        events::{
            key::verification::{
                request::ToDeviceKeyVerificationRequestEvent,
                start::{OriginalSyncKeyVerificationStartEvent, ToDeviceKeyVerificationStartEvent},
            },
            room::message::{MessageType, OriginalSyncRoomMessageEvent},
        },
        UserId,
    },
    Client,
};
use std::io::Write;

pub async fn sas_verification_handler(client: Client, sas: SasVerification) {
    println!(
        "Starting verification with {} {}",
        &sas.other_device().user_id(),
        &sas.other_device().device_id()
    );
    print_devices(sas.other_device().user_id(), &client).await;
    sas.accept().await.unwrap();

    let mut stream = sas.changes();

    while let Some(state) = stream.next().await {
        match state {
            SasState::KeysExchanged {
                emojis,
                decimals: _,
            } => {
                tokio::spawn(wait_for_confirmation(
                    sas.clone(),
                    emojis
                        .expect("We only support verifications using emojis")
                        .emojis,
                ));
            }
            SasState::Done { .. } => {
                let device = sas.other_device();

                println!(
                    "Successfully verified device {} {} {:?}",
                    device.user_id(),
                    device.device_id(),
                    device.local_trust_state()
                );

                print_devices(sas.other_device().user_id(), &client).await;

                break;
            }
            SasState::Cancelled(cancel_info) => {
                println!(
                    "The verification has been cancelled, reason: {}",
                    cancel_info.reason()
                );

                break;
            }
            SasState::Started { .. } | SasState::Accepted { .. } | SasState::Confirmed => (),
        }
    }
}
async fn print_devices(user_id: &UserId, client: &Client) {
    println!("Devices of user {user_id}");

    for device in client
        .encryption()
        .get_user_devices(user_id)
        .await
        .unwrap()
        .devices()
    {
        println!(
            "   {:<10} {:<30} {:<}",
            device.device_id(),
            device.display_name().unwrap_or("-"),
            device.is_verified()
        );
    }
}
async fn wait_for_confirmation(sas: SasVerification, emoji: [Emoji; 7]) {
    println!("\nDo the emojis match: \n{}", format_emojis(emoji));
    print!("Confirm with `yes` or cancel with `no`: ");
    std::io::stdout()
        .flush()
        .expect("We should be able to flush stdout");

    let mut input = String::new();
    std::io::stdin()
        .read_line(&mut input)
        .expect("error: unable to read user input");

    match input.trim().to_lowercase().as_ref() {
        "yes" | "true" | "ok" => sas.confirm().await.unwrap(),
        _ => sas.cancel().await.unwrap(),
    }
}
pub async fn to_device_sync(ev: ToDeviceKeyVerificationRequestEvent, client: Client) -> Result<()> {
    let request = client
        .encryption()
        .get_verification_request(&ev.sender, &ev.content.transaction_id)
        .await
        .expect("Request object wasn't created");

    request
        .accept()
        .await
        .expect("Can't accept verification request");
    Ok(())
}

pub async fn to_device_start_sync(
    ev: ToDeviceKeyVerificationStartEvent,
    client: Client,
) -> Result<()> {
    if let Some(Verification::SasV1(sas)) = client
        .encryption()
        .get_verification(&ev.sender, ev.content.transaction_id.as_str())
        .await
    {
        tokio::spawn(sas_verification_handler(client, sas));
    }
    Ok(())
}

pub async fn original_room_sync(ev: OriginalSyncRoomMessageEvent, client: Client) -> Result<()> {
    if let MessageType::VerificationRequest(_) = &ev.content.msgtype {
        let request = client
            .encryption()
            .get_verification_request(&ev.sender, &ev.event_id)
            .await
            .expect("Request object wasn't created");

        request
            .accept()
            .await
            .expect("Can't accept verification request");
    }
    Ok(())
}

pub async fn original_room_start_sync(
    ev: OriginalSyncKeyVerificationStartEvent,
    client: Client,
) -> Result<()> {
    if let Some(Verification::SasV1(sas)) = client
        .encryption()
        .get_verification(&ev.sender, ev.content.relates_to.event_id.as_str())
        .await
    {
        tokio::spawn(sas_verification_handler(client, sas));
    }
    Ok(())
}
